package cn.springcloud.fix.demo.io;

import java.io.IOException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

public class Acceptor implements Runnable {

    private final ServerSocketChannel ssc; // mainReactor监听的socket通道
    private final int cores = Runtime.getRuntime().availableProcessors(); // 取得CPU核心数
    private final Selector[] selectors = new Selector[cores]; // 创建核心数个selector给subReactor用
    private int selIdx = 0; // 当前可使用的subReactor索引
    private SubReactor[] r = new SubReactor[cores]; // subReactor线程    这里开辟CPU核心数个子线程
    private Thread[] t = new Thread[cores]; // subReactor线程

    public Acceptor(ServerSocketChannel ssc) throws IOException {
        this.ssc = ssc;
        // 创建多个selector以及多个subReactor线程
        for (int i = 0; i < cores; i++) {
            selectors[i] = Selector.open();
            r[i] = new SubReactor(selectors[i], ssc, i);   //Acceptor里面新建一个TCPSubReactor线程组
            t[i] = new Thread(r[i]);
            t[i].start();  //Acceptor构造函数中，这里是TCPSubReactor.start()，表示新建子线程执行TCPSubReactor的run()方法，
            // 但是TCPSubReactor类的run()
        }
    }

    @Override
    public synchronized void run() {   // run()方法要使用同步的，之前都没有用到同步啊  Acceptor的run()方法
        // 为什么Acceptor的run上面使用synchronized 调用这个run的是TCPReactor里面的run()里面的dispatch()方法，直接调用run()方法
        // 而TCPReactor里面的run()，使用Main类中的start()调用
        try {
            SocketChannel sc = ssc.accept(); // 接受client连接请求
            System.out.println(sc.socket().getRemoteSocketAddress().toString() + " is connected.");    // 第三，连接过来之后，这里打印一句
            if (sc != null) {
                sc.configureBlocking(false); // 设置为非阻塞   之前

                // 安排新任务的核心就是将当前的SocketChannel sc注册到指定的selector[selIdx]上面去
                r[selIdx].setRestart(true); // 暂停线程  将selIdx序号TCPSubReactor线程暂停，因为要给他安排新任务
                selectors[selIdx].wakeup(); // 使一个阻塞住的selector操作立即返回，要使用selectors[selIdx]，要将channel注册到这个selectors[selIdx]上面去，
                // 所以要先唤醒它
                SelectionKey sk = sc.register(selectors[selIdx], SelectionKey.OP_READ); // SocketChannel向selector[selIdx]注册一个OP_READ事件，然后返回该通道的key，
                // 这个很重要，表示客户端连接的channel，用来注册到线程池中第一个元素上面  selectors[selIdx]
                // 到底是哪一个元素 selIdx 表示 当前可使用的subReactor索引 ，初始值为0
                selectors[selIdx].wakeup(); // 使一个阻塞住的selector操作立即返回
                r[selIdx].setRestart(false); // 重启线程，将selIdx序号TCPSubReactor线程重启，任务安排好了

                // selectors[selIdx] 和  r[selIdx] 的关系是如何联系在一起的，
                // 回答：不会怎样特别连接，反正Acceptor类的时候，就使用Selector.open();得到一个selector对象，使用 selectors[i] 引用
                // 然后使用这个 selectors[i] 新建一个TcpSubReactor线程   r[i] = new SubReactor(selectors[i], ssc, i);
                // 并执行这个线程     t[i] = new Thread(r[i])  t[i].start();

                //然后，对于run()方法的执行，将新创建的 SocketChannel 注册到 IO 线程池（subReactor 线程池）的某个 IO 线程上，
                // 由它负责SocketChannel 的读写和编解码工作，就是绑定到相应的TCPHandler上面   sk.attach(new Handler(sk, sc));

                // TCPHandler两个参数  一个key 一个客户端channel  默认state为readState  线程最大数量为32
                // TCPHandler里面注入state  state就是读写
                //  很重要1： Acceptor只接受请求，然后就是交给TCPHandler  TCPHandler里面注入state  state就是读写
                // Acceptor 线程池仅仅只用于客户端的登陆、握手和安全
                // 认证，一旦链路建立成功，就将链路注册到后端 subReactor 线程池的 IO 线程上，由 IO 线程负
                //  责后续的 IO 操作。表示Acceptor只接受请求，然后就是交给TCPHandler  TCPHandler里面注入state  state就是读写
                // 很重要2：上面我们注册sc就是为了得到一个key ，这个key就是为构造TCPHandler准备的

                // 这里注册到TCPHandler，那么TCPHandler的run()什么时候执行呢  就是在

                sk.attach(new Handler(sk, sc)); // 给定key一个附加的TCPHandler对象

                if (++selIdx == selectors.length)  // 两种情况解释：如果selIdx++== selectors.length 就是 selIdx== selectors.length -1,就是到了最后一个
                    // 就循环，如果没有到最后一个，不不会执行 selIdx = 0;  反正selIdx还是要++的
                    selIdx = 0;
                //  之前就四句  这里加上而已  什么使用调用SubReactor的run()
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
